#!/usr/bin/env python
# coding: utf-8

# # 1任务描述 
# 利用线性回归技术实现共享单车数量预测
# 

# # 2 数据说明
# 
# Capital Bikesh are（美国Washington, D.C.的一个共享单车公司）提供的共享单车数据。数据包含每天的日期、天气等信息，需要预测每天的共享单车骑行量。
# 
# 1)文件说明
# 
# day.csv: 按天计的单车共享次数（作业只需使用该文件）
# 
# hour.csv: 按小时计的单车共享次数（无需理会）
# 
# readme：数据说明文件
# 
# 2)字段说明
# 
# Instant：记录号
# 
# Dteday：日期
# 
# Season：季节（1=春天、2=夏天、3=秋天、4=冬天）
# 
# yr：年份，(0: 2011, 1:2012)
# 
# mnth：月份( 1 to 12)
# 
# hr：小时 (0 to 23)  （只在hour.csv有，作业忽略此字段）
# 
# holiday：是否是节假日（0/1）
# 
# weekday：星期中的哪天，取值为0～6
# 
# workingday：是否工作日（0/1）
# 
# 1=工作日 （是否为工作日，1为工作日，0为非周末或节假日）
# 
# weathersit：天气（1：晴天，多云 2：雾天，阴天 3：小雪，小雨 4：大雨，大雪，大雾）
# 
# temp：气温摄氏度
# 
# atemp：体感温度
# 
# hum：湿度
# 
# windspeed：风速
# 
# casual：非注册用户贡献的骑行量（作业无需理会该字段）
# 
# registered：注册用户贡献的骑行量（作业无需理会该字段）
# 
# cnt：给定日期（天, day.csv）时间（每小时,hour.csv）总租车人数，响应变量y
# 
# 
# casual、registered和cnt三个特征均为要预测的y（cnt =casual+registered ），作业里只需对cnt进行预测。

# # 3特征工程

# ## 3.1导入相关工具包

# In[2]:


import pandas as pd #SQL数据处理
import numpy as np #矩阵


# ## 3.2数据加载

# In[3]:


#读取数据
data = pd.read_csv("day.csv")

#观察前5行
data.head()


# In[4]:


#查看数据信息
data.info()


# ## 结论：数据完整无缺失

# ## 3.3数据探索

# In[5]:


#对数据特征采取常用统计观察分布值
data.describe()


# In[6]:


#对类别型特征，观察其取值范围
categorical_features = ['season','mnth','weathersit','weekday']
for col in categorical_features:
    print ("\n%s属性的不取值和出现的次数"%col)
    print (data[col].value_counts())
    data[col] = data[col].astype('object')


# ## 3.4特征处理

# In[7]:


#对四个类别进行 one-hot处理 （四个类别取值不多）
x_train_cat = data[categorical_features]
x_train_cat = pd.get_dummies(x_train_cat)
x_train_cat.head()


# In[8]:


#数值型变量预处理 标准化为0~1之间
from sklearn.preprocessing import MinMaxScaler
mn_x = MinMaxScaler()
numerical_features = ['temp','atemp','hum','windspeed']
temp = mn_x.fit_transform(data[numerical_features])

x_train_num = pd.DataFrame(data=temp, columns=numerical_features, index =data.index)
x_train_num.head()


# In[9]:


# Join categorical and numerical features
x_train = pd.concat([x_train_cat, x_train_num, data['holiday'],  data['workingday']], axis = 1, ignore_index=False)
x_train.head()


# In[10]:


FE_train = pd.concat([data['instant'], x_train,  data['yr'],data['cnt']], axis = 1)
FE_train.to_csv('FE_day.csv', index=False)
FE_train.head()


# # 4对特征数据集进行回归分析

# ## 4.1导入工具包

# In[11]:


#导入模型 最小二乘线性回归、岭回归  Lasso
from  sklearn.linear_model  import  LinearRegression,RidgeCV,LassoCV

#模型评估
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score

#绘图
import matplotlib.pyplot as plt


# ## 4.2加载数据

# In[12]:


#导入数据
FE_data = pd.read_csv("FE_day.csv")
#查看数据前5行
FE_data.head()


# ## 4.3选取训练数据以及测试数据

# In[13]:


#数据特征维数 样本数
FE_data.shape


# ### 4.3.1数据准备

# In[46]:


# 从数据中分离输入特征x和输出y
y = FE_data['cnt'].values
X = FE_data.drop('cnt', axis = 1)

#用于后续显示权重系数对应的特征
columns = X.columns

#将数据分割训练数据与测试数据
from sklearn.model_selection import train_test_split

# 随机采样20%的数据构建测试样本，其余作为训练样本
X_train, X_test, y1_train, y1_test = train_test_split(X, y, random_state=33, test_size=0.2)

#训练数据数目 特征维数
X_train.shape



# In[47]:


#查看训练数据
X_train.head()


# In[48]:


#测试数据数目 特征维数
X_test.shape


# In[49]:


#查看测试数据
X_test.head()


# In[55]:


#去训练数据和测试数据的记录号
X1_train = X_train.drop(['instant'],axis = 1)
X1_test = X_test.drop('instant',axis = 1)


# In[51]:


#查看训练数据
X1_train.head()


# In[52]:


#查看测试数据
X1_test.head()


# ### 4.3.2数据标准化

# In[53]:


# 数据标准化
from sklearn.preprocessing import StandardScaler

mean_y_train = y1_train.mean()#原始数据均值
print 'mean_y_train:',mean_y_train

std_y_train=y1_train.std()#原始数据标准差
print 'std_y_train:',std_y_train
      
y1_train=(y1_train-mean_y_train)/std_y_train#标准化后的y1_train
y1_test=(y1_test-mean_y_train)/std_y_train#标准化后的y1_test 

#标准化y_test 的均值

mean_y_test = y1_test.mean()
mean_diff = mean_y_test
print 'mean_diff:',mean_diff


# # 5模型训练和评估

# ## 5.1最小二乘模型

# In[35]:


# 线性回归
from sklearn.linear_model import LinearRegression

# 使用默认配置初始化
lr = LinearRegression()

# 训练模型参数
lr.fit(X1_train, y1_train)

# 预测
y_train_pred_lr = lr.predict(X1_train)
y_test_pred_lr = lr.predict(X1_test)
y_test_pred_lr+=mean_diff


#用于后续显示权重系数对应的特征
columns = X1_train.columns


# In[57]:


#RMSR性能
rmse_train = np.sqrt(mean_squared_error(y1_train,y_train_pred_lr))
rmse_test = np.sqrt(mean_squared_error(y1_test,y_test_pred_lr))
print 'rmse_train:',rmse_train
print 'rmse_test:',rmse_test

#r2_score性能测试
r2_score_train=r2_score(y1_train,y_train_pred_lr)#训练集拟合度
r2_score_test=r2_score(y1_test,y_test_pred_lr)#测试集拟合度
print("r2_score_train:",r2_score_train)
print("r2_score_test:",r2_score_test)


# ## 评价：训练集要好点，测试集稍差

# ## 5.2岭回归模型

# In[42]:


from sklearn.linear_model import  RidgeCV
from sklearn.metrics import r2_score

#设置超参数（正则参数）范围
alphas = [ 0.01, 0.1, 1, 10,100,1000]

#生成一个RidgeCV实例
ridge = RidgeCV(alphas=alphas, store_cv_values=True)  

#模型训练
ridge.fit(X1_train, y1_train)    

#交叉验证得到最佳超参数
alpha = ridge.alpha_
print 'alpha:',alpha

#交叉验证估计的测试误差
mse_cv = np.mean(ridge.cv_values_,axis=0)
rmse_cv = np.sqrt(mse_cv)
print 'cv of rmse:',min(rmse_cv)


#预测
y_train_pred_ridge = ridge.predict(X1_train)
y_test_pred_ridge = ridge.predict(X1_test)
y_test_pred_ridge += mean_diff

#测试误差
rmse_train = np.sqrt(mean_squared_error(y1_train,y_train_pred_ridge))
rmse_test = np.sqrt(mean_squared_error(y1_test,y_test_pred_ridge))

print 'rmse_train:',rmse_train
print 'rmse_test:',rmse_test

r2_score_train = r2_score(y1_train,y_train_pred_ridge)
r2_score_test = r2_score(y1_test,y_test_pred_ridge)

print 'r2_score_train:',r2_score_train
print 'r2_score_test:',r2_score_test




# In[59]:


#绘制重要系数
coefs=pd.Series(ridge.coef_,index=X1_train.columns)

#正系数值最大的10个特征和负系数最小值(绝对值大)的10个特征
imp_coefs=pd.concat([coefs.sort_values().head(10),coefs.sort_values().tail(10)])
imp_coefs.plot(kind="barh")
plt.title("Coefficients in the Ridge Model")
plt.show()

mse_mean=np.mean(ridge.cv_values_,axis=0)
plt.plot(np.log10(alphas),mse_mean.reshape(len(alphas),1))

plt.xlabel('log(alpha)')
plt.ylabel('mse')
plt.show()


# In[ ]:





# ## 评价：训练集要好点，测试集稍差

# ## 5.3Lasso模型

# In[60]:



lasso = LassoCV()

lasso.fit(X1_train,y1_train)

#交叉验证得到最佳超参数
alpha = ridge.alpha_
print 'alpha:',alpha


#绘制重要系数
coefs=pd.Series(lasso.coef_,index=X1_train.columns)#Wj

#正系数值最大的10个特征和负系数最小值(绝对值大)的10个特征
imp_coefs=pd.concat([coefs.sort_values().head(10),coefs.sort_values().tail(10)])
imp_coefs.plot(kind="barh")
plt.title("Coefficients in the Lasso Model")
plt.show()


mses = np.mean(lasso.mse_path_,axis=1)
plt.plot(np.log10(lasso.alphas_),mses)
plt.xlabel('log10(alpha)')
plt.ylabel('mse')
plt.show()
#交叉验证估计的测试误差
mse_cv = np.mean(lasso.mse_path_,axis=0)
rmse_cv = np.sqrt(mse_cv)
print 'cv of rmse:',min(rmse_cv)


#预测
y_train_pred_lasso = lasso.predict(X1_train)
y_test_pred_lasso = lasso.predict(X1_test)
y_test_pred_lasso += mean_diff

#测试误差
rmse_train = np.sqrt(mean_squared_error(y1_train,y_train_pred_lasso))
rmse_test = np.sqrt(mean_squared_error(y1_test,y_test_pred_ridge))

print 'rmse_train:',rmse_train
print 'rmse_test:',rmse_test


r2_score_train = r2_score(y1_train,y_train_pred_lasso)
r2_score_test = r2_score(y1_test,y_test_pred_lasso)

print 'r2_score_train:',r2_score_train
print 'r2_score_test:',r2_score_test


# ## 评价：训练集要好点，测试集稍差

# In[44]:


# 看看各特征的权重系数，系数的绝对值大小可视为该特征的重要性
fs = pd.DataFrame({"columns":list(columns), "coef_lr":list((lr.coef_.T)), "coef_ridge":list((ridge.coef_.T)), "coef_lasso":list((lasso.coef_.T))})
fs.sort_values(by=['coef_lr'],ascending=False)

